#include "bills.h"

#include <utility>
#include <string>
#include <climits>
#include <cstring>
#include <cstdlib>

/*
 * Constructors & destructor
 */

Bills::Bills() : denom(NONE), count(0) {}

Bills::Bills(DENOMINATION denom, unsigned int count) :  denom(denom),
                                                        count(count) {}

Bills::Bills(const Bills &other) {
    this->denom = other.denom;
    this->count = other.count;
}

Bills::~Bills() {}

/*
 * Setters & getters
 */

void Bills::setDenomination(DENOMINATION new_denom) {
    this->denom = new_denom;
}

void Bills::setCount(unsigned int new_count) {
    this->count = new_count;
}

void Bills::setBill(DENOMINATION new_denom, unsigned int new_count) {
    this->denom = new_denom;
    this->count = new_count;
}

void Bills::setBill(std::pair<DENOMINATION, unsigned int> new_bill) {
    this->denom = new_bill.first;
    this->count = new_bill.second;
}

DENOMINATION Bills::getDenomination() {
    return this->denom;
}

unsigned int Bills::getCount() {
    return this->count;
}

std::pair<DENOMINATION, unsigned int> Bills::getBill() {
    return { this->denom, this->count };
}

/*
 * Overloads
 */

Bills & Bills::operator=(const Bills &other) {
    if(this == &other) return *this;

    this->denom = other.denom;
    this->count = other.count;
    return *this;
}

Bills & Bills::operator=(Bills &&other) noexcept {
    if(this == &other) return *this;

    this->denom = std::exchange(other.denom, NONE);
    this->count = std::exchange(other.count, 0);
    return *this;
}

std::ostream & operator<<(std::ostream &os, const Bills &bill) {
    os << std::to_string(bill.denom) << "/" << std::to_string(bill.count);
    return os;
}

std::istream & operator>>(std::istream &is, Bills &bill) {
    std::string s;
    is >> s;
    const char *c_s = s.c_str();
    const char *c_s_end = c_s + std::strlen(c_s);
    char *endptr;
    const char *slash = std::strchr(c_s, '/');
    if(slash == NULL) {
        return is;
    }

    s[slash - c_s] = '\0';

    errno = 0;
    long int denom = std::strtol(c_s, &endptr, 10);
    if(errno == ERANGE || c_s == slash || endptr != slash ||
                          denom != ONE && denom != TWO && denom != FIVE &&
                          denom != TEN && denom != FIFTY && denom != HUNDRED &&
                          denom != FIVE_HUNDRED && denom != THOUSAND &&
                          denom != FIVE_THOUSAND) {
        return is;
    }

    errno = 0;
    long int count = std::strtol(slash + 1, &endptr, 10);
    if(errno == ERANGE || slash + 1 == c_s_end || endptr != c_s_end ||
                          count != static_cast<unsigned int>(count)) {
        return is;
    }

    bill.denom = static_cast<DENOMINATION>(denom);
    bill.count = static_cast<unsigned int>(count);
    return is;
}

/*
 * Public methods (interface)
 */

unsigned int Bills::summa() {
    return this->denom * this->count;
}
